/******************************************************************************
 * Copyright (C) 2015  671643387@qq.com
 *
 * This program is free software; you can redistribute it and/or modify it
 * under the terms of the GNU General Public License as published by the
 * Free Software Foundation; either version 2 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful, but WITHOUT
 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
 * more details.
 *
 * You should have received a copy of the GNU General Public License along
 * with this program; if not, write to the Free Software Foundation, Inc.,
 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
 *
 *****************************************************************************/

#include "Ucop/DBC/DBCLoader.h"

namespace Ucop {
namespace DBC {

	DBCLoader::DBCLoader(void)
		: _MyrecordSize(0)
		, _MyrecordCount(0)
		, _MyfieldCount(0)
		, _MystringSize(0)
		, _MyfieldsOffset(NULL)
		, _Mydata(NULL)
		, _MystringTable(NULL)
	{
		_Mydata = NULL;
		_MyfieldsOffset = NULL;
	}

	DBCLoader::~DBCLoader(void)
	{
		delete[] _Mydata;
		delete[] _MyfieldsOffset;
	}

	DBCLoader::Record DBCLoader::GetRecord(std::size_t _ID)
	{
		UCOP_ASSERT(_Mydata);
		return Record(*this, _Mydata + _ID * _MyrecordSize);
	}

	std::uint32_t DBCLoader::GetFormatRecordSize(const char* _Fmt, std::int32_t* _IndexPos /* = NULL */)
	{
		std::uint32_t _Recordsize = 0;
		std::int32_t _I = -1;
		for (std::uint32_t _X = 0; _Fmt[_X]; ++_X)
		{
			switch (_Fmt[_X])
			{
			case DBC_FF_FLOAT:
				_Recordsize += sizeof(float);
				break;
			case DBC_FF_INT:
				_Recordsize += sizeof(std::uint32_t);
				break;
			case DBC_FF_STRING:
				_Recordsize += sizeof(char*);
				break;
			case DBC_FF_SORT:
				_I = _X;
				break;
			case DBC_FF_IND:
				_I = _X;
				_Recordsize += sizeof(std::uint32_t);
				break;
			case DBC_FF_BYTE:
				_Recordsize += sizeof(std::uint8_t);
				break;
			case DBC_FF_LOGIC:
				UCOP_ASSERT(false && "DBC files not have logic field type");
				break;
			case DBC_FF_NA:
			case DBC_FF_NA_BYTE:
				break;
			default:
				UCOP_ASSERT(false && "unknown format character");
				break;
			}
		}

		if (_IndexPos) {
			*_IndexPos = _I;
		}

		return _Recordsize;
	}

	char* DBCLoader::AutoProduceData(const char* _Fmt, std::uint32_t& _Count, char**& _IndexTable)
	{
		/*
		format STRING, NA, FLOAT,NA,INT <=>
		struct{
		char* field0,
		float field1,
		int field2
		}entry;

		this func will generate  entry[rows] data;
		*/

		typedef char* _Ptr;
		if (strlen(_Fmt) != _MyfieldCount)
		{
			return NULL;
		}

		// get struct size and index pos
		std::int32_t _I;
		std::uint32_t _Recordsize = GetFormatRecordSize(_Fmt, &_I);

		if (_I >= 0)
		{
			std::uint32_t _Maxi = 0;
			// find max index
			for (std::uint32_t _Y = 0; _Y < _MyrecordCount; ++_Y)
			{
				std::uint32_t _Ind = GetRecord(_Y).GetUInt(_I);
				if (_Ind > _Maxi) { _Maxi = _Ind; }
			}

			++_Maxi;
			_Count = _Maxi;
			_IndexTable = new _Ptr[_Maxi];
			memset(_IndexTable, 0, _Maxi * sizeof(_Ptr));
		}
		else
		{
			_Count = _MyrecordCount;
			_IndexTable = new _Ptr[_MyrecordCount];
		}

		char* _DataTable = new char[_MyrecordCount * _Recordsize];

		std::uint32_t _Offset = 0;

		for (std::uint32_t _Y = 0; _Y < _MyrecordCount; ++_Y)
		{
			if (_I >= 0)
			{
				_IndexTable[GetRecord(_Y).GetUInt(_I)] = &_DataTable[_Offset];
			}
			else
			{
				_IndexTable[_Y] = &_DataTable[_Offset];
			}

			for (std::uint32_t _X = 0; _X < _MyfieldCount; ++_X)
			{
				switch (_Fmt[_X])
				{
				case DBC_FF_FLOAT:
					*((float*)(&_DataTable[_Offset])) = GetRecord(_Y).GetFloat(_X);
					_Offset += sizeof(float);
					break;
				case DBC_FF_IND:
				case DBC_FF_INT:
					*((std::uint32_t*)(&_DataTable[_Offset])) = GetRecord(_Y).GetUInt(_X);
					_Offset += sizeof(std::uint32_t);
					break;
				case DBC_FF_BYTE:
					*((std::uint8_t*)(&_DataTable[_Offset])) = GetRecord(_Y).GetUInt8(_X);
					_Offset += sizeof(std::uint8_t);
					break;
				case DBC_FF_STRING:
					*((char**)(&_DataTable[_Offset])) = NULL; // will be replaces non-empty or "" strings in AutoProduceStrings
					_Offset += sizeof(char*);
					break;
				case DBC_FF_LOGIC:
					UCOP_ASSERT(false && "DBC files not have logic field type");
					break;
				case DBC_FF_NA:
				case DBC_FF_NA_BYTE:
				case DBC_FF_SORT:
					break;
				default:
					UCOP_ASSERT(false && "unknown format character");
					break;
				}
			}
		}

		return _DataTable;
	}

	char* DBCLoader::AutoProduceStrings(const char* _Fmt, char* _DataTable)
	{
		if (strlen(_Fmt) != _MyfieldCount)
		{
			return NULL;
		}

		char* _StringPool = new char[_MystringSize];
		memcpy(_StringPool, _MystringTable, _MystringSize);

		std::uint32_t _Offset = 0;

		for (std::uint32_t _Y = 0; _Y < _MyrecordCount; ++_Y)
		{
			for (std::uint32_t _X = 0; _X < _MyfieldCount; ++_X)
			{
				switch (_Fmt[_X])
				{
				case DBC_FF_FLOAT:
					_Offset += sizeof(float);
					break;
				case DBC_FF_IND:
				case DBC_FF_INT:
					_Offset += sizeof(std::uint32_t);
					break;
				case DBC_FF_BYTE:
					_Offset += sizeof(std::uint8_t);
					break;
				case DBC_FF_STRING:
				{
					// fill only not filled entries
					char** _Slot = (char**)(&_DataTable[_Offset]);
					if (!*_Slot || !** _Slot)
					{
						const char* _St = GetRecord(_Y).GetString(_X);
						*_Slot = _StringPool + (_St - (const char*)_MystringTable);
					}
					_Offset += sizeof(char*);
					break;
				}
				case DBC_FF_LOGIC:
					UCOP_ASSERT(false && "DBC files not have logic field type");
					break;
				case DBC_FF_NA:
				case DBC_FF_NA_BYTE:
				case DBC_FF_SORT:
					break;
				default:
					UCOP_ASSERT(false && "unknown format character");
					break;
				}
			}
		}

		return _StringPool;
	}

}}